Close save-after-restore race.
authorjohn.levon@sun.com <john.levon@sun.com>
Wed, 24 Jan 2007 03:06:31 +0000 (19:06 -0800)
committerjohn.levon@sun.com <john.levon@sun.com>
Wed, 24 Jan 2007 03:06:31 +0000 (19:06 -0800)
Make xc_linux_save() wait for the frame_list_list MFN to be updated by the
domain before trying to use it. Make Linux set the top-level MFN /after/
updating the other MFN lists.

Signed-off-by: John Levon <john.levon@sun.com>
linux-2.6-xen-sparse/arch/i386/kernel/setup-xen.c
linux-2.6-xen-sparse/arch/x86_64/kernel/setup-xen.c
linux-2.6-xen-sparse/drivers/xen/core/machine_reboot.c
tools/libxc/xc_linux_save.c

index 946ecffb8dbb5701b20878a4ce30c3b9b1e643cd..4d0d6e05584ce6ffde6c3e762dbf9cfe59ab30e9 100644 (file)
@@ -1736,8 +1736,6 @@ void __init setup_arch(char **cmdline_p)
                 * frames that make up the p2m table. Used by save/restore
                 */
                pfn_to_mfn_frame_list_list = alloc_bootmem_low_pages(PAGE_SIZE);
-               HYPERVISOR_shared_info->arch.pfn_to_mfn_frame_list_list =
-                    virt_to_mfn(pfn_to_mfn_frame_list_list);
 
                fpp = PAGE_SIZE/sizeof(unsigned long);
                for (i=0, j=0, k=-1; i< max_pfn; i+=fpp, j++) {
@@ -1754,6 +1752,8 @@ void __init setup_arch(char **cmdline_p)
                                virt_to_mfn(&phys_to_machine_mapping[i]);
                }
                HYPERVISOR_shared_info->arch.max_pfn = max_pfn;
+               HYPERVISOR_shared_info->arch.pfn_to_mfn_frame_list_list =
+                    virt_to_mfn(pfn_to_mfn_frame_list_list);
        }
 
        /*
index 8b3b67ad959f76a513e9b9c9c296392de9d61f45..f49110cf904e7e5958eeed0e800dc2985e95a1c1 100644 (file)
@@ -817,8 +817,6 @@ void __init setup_arch(char **cmdline_p)
                          * save/restore.
                         */
                        pfn_to_mfn_frame_list_list = alloc_bootmem_pages(PAGE_SIZE);
-                       HYPERVISOR_shared_info->arch.pfn_to_mfn_frame_list_list =
-                               virt_to_mfn(pfn_to_mfn_frame_list_list);
 
                        fpp = PAGE_SIZE/sizeof(unsigned long);
                        for (i=0, j=0, k=-1; i< end_pfn; i+=fpp, j++) {
@@ -835,6 +833,8 @@ void __init setup_arch(char **cmdline_p)
                                        virt_to_mfn(&phys_to_machine_mapping[i]);
                        }
                        HYPERVISOR_shared_info->arch.max_pfn = end_pfn;
+                       HYPERVISOR_shared_info->arch.pfn_to_mfn_frame_list_list =
+                               virt_to_mfn(pfn_to_mfn_frame_list_list);
                }
 
        }
index b69efd61f97fd6d536da0a07787ea839079c8429..07de13c835500e7fc8906599cd1c30a5555549fd 100644 (file)
@@ -105,9 +105,6 @@ static void post_suspend(int suspend_cancelled)
 
        memset(empty_zero_page, 0, PAGE_SIZE);
 
-       HYPERVISOR_shared_info->arch.pfn_to_mfn_frame_list_list =
-               virt_to_mfn(pfn_to_mfn_frame_list_list);
-
        fpp = PAGE_SIZE/sizeof(unsigned long);
        for (i = 0, j = 0, k = -1; i < max_pfn; i += fpp, j++) {
                if ((j % fpp) == 0) {
@@ -120,6 +117,8 @@ static void post_suspend(int suspend_cancelled)
                        virt_to_mfn(&phys_to_machine_mapping[i]);
        }
        HYPERVISOR_shared_info->arch.max_pfn = max_pfn;
+       HYPERVISOR_shared_info->arch.pfn_to_mfn_frame_list_list =
+               virt_to_mfn(pfn_to_mfn_frame_list_list);
 }
 
 #else /* !(defined(__i386__) || defined(__x86_64__)) */
index ef64d1ef50bccd6921175cf93a2e0f21f9da2c86..047bbf7e95f0677fff292f4be9adaffcb2610e57 100644 (file)
@@ -404,6 +404,33 @@ static int suspend_and_state(int (*suspend)(int), int xc_handle, int io_fd,
     return -1;
 }
 
+/*
+** Map the top-level page of MFNs from the guest. The guest might not have
+** finished resuming from a previous restore operation, so we wait a while for
+** it to update the MFN to a reasonable value.
+*/
+static void *map_frame_list_list(int xc_handle, uint32_t dom,
+                                 shared_info_t *shinfo)
+{
+    int count = 100;
+    void *p;
+
+    while (count-- && shinfo->arch.pfn_to_mfn_frame_list_list == 0)
+        usleep(10000);
+
+    if (shinfo->arch.pfn_to_mfn_frame_list_list == 0) {
+        ERROR("Timed out waiting for frame list updated.");
+        return NULL;
+    }
+
+    p = xc_map_foreign_range(xc_handle, dom, PAGE_SIZE, PROT_READ,
+                             shinfo->arch.pfn_to_mfn_frame_list_list);
+
+    if (p == NULL)
+        ERROR("Couldn't map p2m_frame_list_list (errno %d)", errno);
+
+    return p;
+}
 
 /*
 ** During transfer (or in the state file), all page-table pages must be
@@ -669,14 +696,11 @@ int xc_linux_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters,
 
     max_pfn = live_shinfo->arch.max_pfn;
 
-    live_p2m_frame_list_list =
-        xc_map_foreign_range(xc_handle, dom, PAGE_SIZE, PROT_READ,
-                             live_shinfo->arch.pfn_to_mfn_frame_list_list);
+    live_p2m_frame_list_list = map_frame_list_list(xc_handle, dom,
+                                                   live_shinfo);
 
-    if (!live_p2m_frame_list_list) {
-        ERROR("Couldn't map p2m_frame_list_list (errno %d)", errno);
+    if (!live_p2m_frame_list_list)
         goto out;
-    }
 
     live_p2m_frame_list =
         xc_map_foreign_batch(xc_handle, dom, PROT_READ,
@@ -1175,8 +1199,14 @@ int xc_linux_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters,
     ctxt.ctrlreg[3] = 
         xen_pfn_to_cr3(mfn_to_pfn(xen_cr3_to_pfn(ctxt.ctrlreg[3])));
 
+    /*
+     * Reset the MFN to be a known-invalid value. See map_frame_list_list().
+     */
+    memcpy(page, live_shinfo, PAGE_SIZE);
+    ((shared_info_t *)page)->arch.pfn_to_mfn_frame_list_list = 0;
+
     if (!write_exact(io_fd, &ctxt, sizeof(ctxt)) ||
-        !write_exact(io_fd, live_shinfo, PAGE_SIZE)) {
+        !write_exact(io_fd, page, PAGE_SIZE)) {
         ERROR("Error when writing to state file (1) (errno %d)", errno);
         goto out;
     }